﻿/*
 *	Copyright (c) 2009 Queensland University of Technology. All rights reserved.
 *	The QUT Bioinformatics Collection is open source software released under the 
 *	Microsoft Public License (Ms-PL): http://www.microsoft.com/opensource/licenses.mspx.
 */
using System;
using System.ComponentModel;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace QUT.Bio.SilverMap {

	/// <summary>
	///	A stretcher applies an exponential scaling to an input value to allow us to
	///	exaggerate distances close to the origin, which is often where the action is
	///	in a BLAST result set.
	/// </summary>

	public class Stretcher : INotifyPropertyChanged {
		private double defaultStretch = 0.5;
		private double stretch;
		private double minimum = 0.01;
		private double maximum = 5;

		#region Event: PropertyChanged
		/// <summary>
		/// Satisfy the INotifyPropertychanged interface to enable data binding.
		/// </summary>
		
		public event PropertyChangedEventHandler PropertyChanged;	
		#endregion		

		#region Property: Stretch
		/// <summary>
		/// Get or set the 'stiffness' of this stretcher.
		/// <para>Contract for setter:</para>
		/// <para>Pre: true.</para>
		/// <para>Post: Propertychanged listeners have been notified.</para>
		/// </summary>

		public double Stretch {
			get {
				return stretch;
			}
			set {
				stretch = value;
				NotifyPropertyChanged( "Stretch" );
			}
		} 
		#endregion
		
		#region Property: DefaultStretch
		/// <summary>
		/// Get or set the default stiffness (Stretch) used by Reset().
		/// </summary>
		
		public double DefaultStretch {
			get {
				return defaultStretch;
			}
			set {
				defaultStretch = value;
			}
		}
		#endregion

		#region Property: Minimum
		/// <summary>
		/// Get or set the minimum value used in the strech transformation.
		/// <para>Contract for set operation.</para>
		/// <para>Pre: true.</para>
		/// <para>Post: supplied value has been saved and PropertyChanged listeners have been notified.</para>
		/// </summary>
		
		public double Minimum {
			get {
				return minimum;
			}
			set {
				minimum = value;
				NotifyPropertyChanged( "Minimum" );
			}
		} 
		#endregion

		#region Property: Maximum
		/// <summary>
		/// Get or set the maximum value used in the strech transformation.
		/// <para>Contract for set operation:</para>
		/// <para>Pre: true.</para>
		/// <para>Post: supplied value has been saved and PropertyChanged listeners have been notified.</para>
		/// </summary>
		
		public double Maximum {
			get {
				return maximum;
			}
			set {
				maximum = value;
				NotifyPropertyChanged( "Maximum" );
			}
		}
		#endregion

		#region Property: StretchLiteral
		/// <summary>
		/// Get a literal representation of the stretch which can be used to display the current value.
		/// </summary>

		public string StretchLiteral {
			get {
				return String.Format( "{0:f2}", stretch );
			}
		}
		#endregion

		#region Method: NotifyPropertyChanged
		/// <summary>
		/// Notifies any registered listeners that the value of a specified field has been changed.
		/// </summary>
		/// <param name="field">The property name used to access the field.</param>
		
		protected void NotifyPropertyChanged ( string field ) {
			if ( PropertyChanged != null ) {
				PropertyChanged( this, new PropertyChangedEventArgs( field ) );
			}
		} 
		#endregion

		#region Method: StretchOut
		/// <summary>
		/// Used by Normalizer objects to transform input value x by 
		/// raising x to a monotonic increasing function of 'stretch'.
		/// <para>If stretch is less than 0.2, this pushes things away from the origin.</para>
		/// <para>If stretch is greater than 0.2, this pulls things toward the origin.</para>
		/// </summary>
		/// <param name="x">The value to be transformed.</param>
		/// <returns>A scaled input value.</returns>
		
		public double StretchOut ( double x ) {
			double scale = stretch > 0.5 ? stretch * stretch * 10 : 5 * stretch;

			return Math.Pow( x, scale );
		} 
		#endregion

		#region Method: StretchIn
		/// <summary>
		/// The inverse of StretchOut.
		/// </summary>
		/// <param name="x">The value to be transformed.</param>
		/// <returns>A scaled input value.</returns>
		
		
		public double StretchIn ( double x ) {
			double scale = stretch > 0.5 ? stretch * stretch * 10 : 5 * stretch;

			return Math.Pow( x, 1 / scale );
		} 
		#endregion

		#region Method: Reset
		/// <summary>
		/// Resets the stretch value to its default.
		/// <para>Pre: true.</para>
		/// <para>Post: Stiffness (stretch) has been reset to the default value and PropertyChanged listeners have been notified.</para>
		/// </summary>

		public void Reset () {
			Stretch = defaultStretch;
		} 
		#endregion
	}
}
